#ifndef CAMERA_H
#define CAMERA_H

#include <QMatrix4x4>
#include <math.h>
#include <vector>

// 移动方向枚举量，是一种抽象，以避开特定于窗口系统的输入方法
// 我们这里是WSAD
enum Camera_Movement {
    FORWARD,  //
    BACKWARD, //
    LEFT,     //
    RIGHT
};

// 默认值
const float YAW         = -90.0f;
const float PITCH       = 0.0f;
const float SPEED       = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM        = 45.0f;
const float PI          = 3.1415926;

// 一个抽象的camera类，用于处理输入并计算相应的euler角度、向量和矩阵，以便在opengl中使用
class Camera {
  public:
    // camera Attributes
    QVector3D Position;
    QVector3D Front;
    QVector3D Up;
    QVector3D Right;
    QVector3D WorldUp;

    // euler Angle
    float Yaw   = YAW;
    float Pitch = PITCH;

    // camera options   control the scorl
    float MovementSpeed  = SPEED;
    float MouseSensitive = SENSITIVITY;
    float Zoom           = ZOOM;

    // constracor with vector
    Camera( QVector3D position = QVector3D( 0.0f, 0.0f, 0.0f ), QVector3D up = QVector3D( 0.0f, 1.0f, 0.0f ),
            float yaw = YAW, float pitch = PITCH ) {
        Position = position;
        WorldUp  = up;
        Yaw      = yaw;
        Pitch    = pitch;

        updateCameraVectors();
    }

    // constractor with scalar values
    Camera( float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw = YAW,
            float pitch = PITCH ) {
        Position = QVector3D( posX, posY, posZ );
        WorldUp  = QVector3D( upX, upY, upZ );
        Yaw      = yaw;
        Pitch    = pitch;
        updateCameraVectors();
    }

    // return the view matrix calculated using eular angeles and the lockar matrixx
    QMatrix4x4 GetViewMatrix() {
        QMatrix4x4 theMatrix;
        theMatrix.lookAt( Position, Position + Front, Up );
        return theMatrix;
    }

    // custome implementation of the lockat function
    QMatrix4x4 calculate_lockAt_matrix( QVector3D position, QVector3D target, QVector3D worldUp ) {
        // 1 Position = known
        // 2 calculate cameraDireaction
        QVector3D zaxis = QVector3D( position - target );
        zaxis.normalize();
        // 3 get position right axis vector
        worldUp.normalize();
        QVector3D xaxis = QVector3D::crossProduct( worldUp, zaxis );
        xaxis.normalize();
        // 4 calculate camera up vector
        QVector3D yaxis = QVector3D::crossProduct( zaxis, xaxis );
        // create translation and rotation mattix

        QMatrix4x4 translation; // identity matrix by default
        QVector4D  theVector4D;
        theVector4D.setX( -position.x() ); // third column ,first row
        theVector4D.setY( -position.y() );
        theVector4D.setZ( -position.z() );
        theVector4D.setW( 1.0 );
        translation.setColumn( 3, theVector4D );

        QMatrix4x4 rotation;
        theVector4D.setX( xaxis.x() ); // third column ,first row
        theVector4D.setY( xaxis.y() );
        theVector4D.setZ( xaxis.z() );
        theVector4D.setW( 0.0 );
        rotation.setColumn( 0, theVector4D );

        theVector4D.setX( yaxis.x() ); // third column ,first row
        theVector4D.setY( yaxis.y() );
        theVector4D.setZ( yaxis.z() );
        theVector4D.setW( 0.0 );
        rotation.setColumn( 1, theVector4D );

        theVector4D.setX( zaxis.x() ); // third column ,first row
        theVector4D.setY( zaxis.y() );
        theVector4D.setZ( zaxis.z() );
        theVector4D.setW( 0.0 );
        rotation.setColumn( 2, theVector4D );

        return rotation * translation;
    }

    // 处理从任何类似键盘的输入系统接受输入，接受摄像机定义的枚举形式的输入参数（从窗口系统中提取）
    void ProcessKeyboard( Camera_Movement direction, float deltaTime ) {
        float velocity = MovementSpeed * deltaTime;
        switch ( direction ) {
        case FORWARD: {
            Position += Front * velocity;
            break;
        }
        case BACKWARD: {
            Position -= Front * velocity;
            break;
        }
        case LEFT: {
            Position -= Right * velocity;
            break;
        }
        case RIGHT: {
            Position += Right * velocity;
            break;
        }
        default:
            break;
        }
    }

    // 处理鼠标输入系统接受的输入，需要x和y方向的偏移值
    void ProcessMouseMovement( float xoffset, float yoffset ) {
        xoffset *= MouseSensitive;
        yoffset *= MouseSensitive;

        Yaw += xoffset;
        Pitch += yoffset;

        // 确保当前投球超出边界时，屏幕不会翻转
        if ( Pitch > 90.0f ) {
            Pitch = 89.0f;
        }
        if ( Pitch < -89.0f ) {
            Pitch = -89.0f;
        }

        // 使用更新的euler角度更新前、右和上矢量
        updateCameraVectors();
    }

    //处理从鼠标滚轮事件接受的输入 ，仅需要在锤子车轮轴上的输入
    void PrecessMouseScroll( float yoffset ) {
        if ( Zoom >= 1.0f && Zoom <= 75.0f ) {
            Zoom -= (float) yoffset;
        }
        if ( Zoom <= 1.0f ) {
            Zoom = 1.0f;
        }
        if ( Zoom >= 75.0f ) {
            Zoom = 75.0f;
        }
    }

  private:
    // 更具相机的更新 euler角度计算前矢量
    void updateCameraVectors() {
        // calculate the new front vector
        QVector3D front;
        front.setX( cos( Yaw * PI / 180.0 ) * cos( Pitch * PI / 180.0 ) );
        front.setY( sin( Pitch * PI / 180.0 ) );
        front.setZ( sin( Yaw * PI / 180.0 ) * cos( Pitch * PI / 180.0 ) );
        front.normalize();
        Front = front;

        // slso re-caculate the Rigth and Up vector
        // 标准化向量，因为向上或者向下看的越多，向量的长度就越接近0，这会导致移动变慢
        Right = QVector3D::crossProduct( Front, WorldUp );
        Right.normalize();
        Up = QVector3D::crossProduct( Right, Front );
        Up.normalize();
    }
};

#endif // CAMERA_H
